package com.lock.redisLock;

import com.google.common.base.Preconditions;
import com.lock.RLock;
import com.lock.util.LockUtilsConfig;
import io.lettuce.core.SetArgs;
import io.lettuce.core.cluster.api.sync.RedisClusterCommands;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Redis批量锁
 *
 * @param <T>
 */
public class RedisBatchLock<T extends Object> implements RLock {

    private static final Logger log = LoggerFactory.getLogger(RedisBatchLock.class);
    private static final String LOCKNAME_PREFIX = "dal_rlock_";
    private boolean locked = false;
    private Map<String, T> lockKey;
    private RedisClusterCommands<String, String> lettuce = null;

    public RedisBatchLock() {
        this.lettuce = LockUtilsConfig.getRedisCmd();
    }

    @Override
    public void close() {
        this.unlock();
    }

    public List<T> batchLock(String prefix, List<T> nameList, long lockExpireTime) {
        Map<String, T> tmpLockKey = new HashMap<>();
        for (T name : nameList) {
            tmpLockKey.put(LOCKNAME_PREFIX + LockUtilsConfig.GOLBAL_LOCK_NAME_PREFIX + prefix + name.toString(), name);
        }

        this.lockKey = tmpLockKey;

        return this.tryLockInner(lockExpireTime, TimeUnit.SECONDS);
    }

    @Override
    public void lock() throws Exception {
    }

    @Override
    public boolean tryLock() throws Exception {
        return false;
    }

    @Override
    public boolean tryLock(Duration waitTime) throws Exception {
        return false;
    }

    @Override
    public boolean tryLock(long waitTime, TimeUnit waitUnit) throws Exception {
        return false;
    }

    @Override
    public boolean tryLock(Duration waitTime, Duration leaseTime) throws Exception {
        return false;
    }

    @Override
    public boolean tryLock(long waitTime, TimeUnit waitUnit, long leaseTime, TimeUnit leaseUnit) throws Exception {
        return false;
    }

    private List<T> tryLockInner(long leaseTime, TimeUnit leaseUnit) {
        Preconditions.checkNotNull(this.lettuce != null, "lettuce is null");
        long lockLeaseTime = leaseUnit.toMillis(leaseTime);
        boolean doDelete = false;

        Map<String, T> tmpLockKey = new HashMap<>();
        try {
            for (Map.Entry<String, T> entry : this.lockKey.entrySet()) {
                String rtnCode = this.lettuce.set(entry.getKey(), "1", SetArgs.Builder.nx().px(lockLeaseTime));
                if (StringUtils.equals(rtnCode, "OK")) {
                    tmpLockKey.put(entry.getKey(), entry.getValue());
                }
            }

        } catch (Exception var21) {
            doDelete = true;
            this.locked = true;
            throw var21;
        } finally {
            if (doDelete) {
                this.unlock();
            }

        }

        this.lockKey = tmpLockKey;

        this.locked = true;

        return new ArrayList<>(tmpLockKey.values());
    }

    @Override
    public void unlock() {
        if (this.locked) {
            for (Map.Entry<String, T> key : this.lockKey.entrySet()) {
                this.lettuce.del(new String[]{key.getKey()});
            }
        }

        this.locked = false;
    }

    @Override
    public boolean isLocked() {
        return this.locked;
    }

    @Override
    public boolean isHoldByCurrentThread() {
        return this.locked;
    }

    @Override
    public int getHoldCount() {
        return 1;
    }
}
